#include "SoundEngine.h"
#include "stdio.h"
#include <map>
#include <stack>
#include <string>
#include <AL/alut.h>

using namespace std;
using namespace sound;

typedef map< std::string, ALuint > NameToAluint;
typedef map< const Speaker*, ALuint > SpeakerToAluint;

const uint SPEAKERS_COUNT = 16;
const uint BUFFERS_COUNT = 256;

ALuint _speakerIDs[ SPEAKERS_COUNT ] = { -1, };
ALuint _bufferIDs[ BUFFERS_COUNT ] = { -1, };

stack<ALuint> _freeSourceIDs;

NameToAluint _nameToBufferID;
SpeakerToAluint _speakerToSourceID;

void CheckForErrors( ) {
	ALuint error = alutGetError( );
	if( error != ALUT_ERROR_NO_ERROR ) {
		printf( alutGetErrorString( error ) );
		throw alutGetErrorString( error );
	}
}

void SoundEngine::Create( ) {
	alutInit( 0, NULL );
	alGenBuffers( BUFFERS_COUNT, _bufferIDs );
	alGenSources( SPEAKERS_COUNT, _speakerIDs );
	for( uint i = 0; i < SPEAKERS_COUNT; ++i )
		_freeSourceIDs.push( _speakerIDs[i] );

	CheckForErrors( );
}

void SoundEngine::Destroy( ) {
	while( _freeSourceIDs.size( ) > 0 )
		_freeSourceIDs.pop( );
	alDeleteSources( SPEAKERS_COUNT, _speakerIDs );
	alDeleteBuffers( BUFFERS_COUNT, _bufferIDs );
	alutExit( );

	CheckForErrors( );
}

void SoundEngine::RegisterSpeaker( const Speaker& speaker ) {
	printf( "Registering speaker...\n" );

	_speakerToSourceID[ &speaker ] = _freeSourceIDs.top( );
	_freeSourceIDs.pop( );
	Update( speaker );

	CheckForErrors( );
	printf( "Speaker registered\n" );
}

void SoundEngine::UnregisterSpeaker( const Speaker& speaker ) {
	printf( "Unregistering speaker...\n" );
	_freeSourceIDs.push( _speakerToSourceID[ &speaker ] );
	_speakerToSourceID[ &speaker ] = -1;
	CheckForErrors( );
	printf( "Speaker unregistered\n" );
}

bool SoundEngine::BufferIsRegistered( const std::string& bufferName ) {
	printf( "bufferID == %d\n", _nameToBufferID[ bufferName ] );
	return _nameToBufferID[ bufferName ] != 0;
}

void SoundEngine::RegisterBuffer( const std::string& bufferName ) {
	char* fileName = const_cast<char*> ( bufferName.c_str( ) );
	printf( "Registering buffer %s\n", fileName );

	_nameToBufferID[ bufferName ] = alutCreateBufferFromFile( fileName );
	CheckForErrors( );
	printf( "Buffer registered\n" );
}

void SoundEngine::UnregisterBuffer( const std::string& bufferName ) {
	_nameToBufferID[ bufferName ] = -1;
	CheckForErrors( );
	printf( "Buffer unregistered\n" );
}

void SoundEngine::Play( const Speaker& speaker, const string& bufferName ) {
	printf( "Play starting...\n" );
	if( !BufferIsRegistered( bufferName ) )
		RegisterBuffer( bufferName );

	ALuint sourceID = _speakerToSourceID[ &speaker ];
	ALuint bufferID = _nameToBufferID[ bufferName ];

	printf( "source: %d; buffer: %d\n", sourceID, bufferID );

	alSourcei( sourceID, AL_BUFFER, bufferID );
	alSourcePlay( sourceID );
	CheckForErrors( );
	printf( "Play started\n" );
}

void SoundEngine::Stop( const Speaker& speaker ) {
	ALuint sourceID = _speakerToSourceID[ &speaker ];
	alSourceStop( sourceID );
	CheckForErrors( );
}

void SoundEngine::Pause( const Speaker& speaker ) {
	ALuint sourceID = _speakerToSourceID[ &speaker ];
	alSourcePause( sourceID );
	CheckForErrors( );
}

void SoundEngine::Update( const Speaker& speaker ) {
	ALuint sourceID = _speakerToSourceID[ &speaker ];
	alSource3f( sourceID, AL_POSITION, speaker.GetPos( ).X, speaker.GetPos( ).Y, 0.0f );
	alSource3f( sourceID, AL_VELOCITY, speaker.GetVel( ).X, speaker.GetVel( ).Y, 0.0f );
	CheckForErrors( );
}

void SoundEngine::SetListener( const Vec2& position, const Vec2& orientaion, const Vec2& velocity ) {
	alListener3f( AL_POSITION, position.X, position.Y, 0.0f );

	ALfloat listenerOri[]={0.0,0.0,-1.0, orientaion.X,orientaion.Y,0.0}; 
	alListenerfv( AL_ORIENTATION, listenerOri );

	alListener3f( AL_VELOCITY, velocity.X, velocity.Y, 0.0f );
	CheckForErrors( );
}
